package org.wmq.positiontoshop.controller;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.wmq.positiontoshop.common.LocalCache;
import org.wmq.positiontoshop.common.MinioClient;
import org.wmq.positiontoshop.common.Result;
import org.wmq.positiontoshop.config.ApplicationConfiguration;
import org.wmq.positiontoshop.model.DO.Shop;
import org.wmq.positiontoshop.model.ES.ShopES;
import org.wmq.positiontoshop.service.BloomFilterService;
import org.wmq.positiontoshop.service.ElasticsearchSearchService;
import org.wmq.positiontoshop.service.ElasticsearchSyncService;
import org.wmq.positiontoshop.service.ShopService;

import java.io.IOException;
import java.util.List;
import java.util.UUID;

/**
 * 店铺控制器
 * 提供店铺相关的API接口
 */
@RestController
@RequestMapping("/shop")
@Slf4j
public class ShopController {
    
    @Autowired
    private ShopService shopService;
    
    @Autowired
    private ElasticsearchSearchService elasticsearchSearchService;
    
    @Autowired
    private ElasticsearchSyncService elasticsearchSyncService;
    @Autowired
    private LocalCache localCache;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private KafkaTemplate<String,String> kafkaTemplate;
    @Autowired
    private BloomFilterService bloomFilterService;
    @Autowired
    private MinioClient minioClient;
    @Autowired
    private ApplicationConfiguration applicationConfiguration;
    
    /**
     * 根据ID查询店铺
     * 
     * @param id 店铺ID
     * @return 店铺信息
     */
    @GetMapping("/{id}")
    public Result<Shop> getShopById(@PathVariable Long id) {
        boolean valid = this.bloomFilterService.mightContainShopId(id);
        if (!valid){
            log.error("店铺不存在，id：{},该请求被BloomFilter过滤", id);
            return Result.error("店铺不存在");
        }
        String key = "shop:" + id;
        String shopJson = this.localCache.get(key);
        if (shopJson != null) {
            return Result.success(JSON.parseObject(shopJson, Shop.class));
        }
        shopJson = this.redisTemplate.opsForValue().get(key);
        if (StringUtils.hasLength(shopJson)){
            this.localCache.put(key, shopJson);
            return Result.success(JSON.parseObject(shopJson, Shop.class));
        }
        Shop shop = shopService.getById(id);
        if (shop == null) {
            log.info("Shop not found, id={}", id);
            return Result.error("SHOP_NOT_FOUND");
        }
        shopJson = JSON.toJSONString(shop);
        this.localCache.put(key, shopJson);
        this.redisTemplate.opsForValue().set(key, shopJson);
        return Result.success(shop);
    }
    
    /**
     * 模糊搜索店铺（基于Elasticsearch）
     * 
     * @param keyword 搜索关键词
     * @return 匹配的店铺列表
     */
    @GetMapping("/search")
    public Result<List<ShopES>> searchShops(@RequestParam String keyword) {
        log.info("模糊搜索店铺，关键词：{}", keyword);
        List<ShopES> shops = elasticsearchSearchService.searchShops(keyword);
        return Result.success(shops);
    }
    
    /**
     * 高级搜索店铺（基于Elasticsearch）
     * 
     * @param keyword 搜索关键词
     * @param type 店铺类型
     * @param minRating 最低评分
     * @param status 店铺状态
     * @return 匹配的店铺列表
     */
    @GetMapping("/advanced-search")
    public Result<List<ShopES>> advancedSearch(
            @RequestParam(required = false) String keyword,
            @RequestParam(required = false) String type,
            @RequestParam(required = false) Double minRating,
            @RequestParam(required = false) Integer status) {
        log.info("高级搜索店铺，关键词：{}, 类型：{}, 最低评分：{}, 状态：{}", 
                keyword, type, minRating, status);
        List<ShopES> shops = elasticsearchSearchService.advancedSearch(keyword, type, minRating, status);
        return Result.success(shops);
    }
    
    /**
     * 搜索附近的店铺（基于Elasticsearch地理位置搜索）
     * 
     * @param latitude 纬度
     * @param longitude 经度
     * @param distance 距离（单位：km）
     * @return 匹配的店铺列表
     */
    @GetMapping("/es-nearby")
    public Result<List<ShopES>> searchNearbyShops(
            @RequestParam Double latitude,
            @RequestParam Double longitude,
            @RequestParam(defaultValue = "5") Double distance) {
        log.info("搜索附近的店铺，纬度：{}, 经度：{}, 距离：{}km", 
                latitude, longitude, distance);
        List<ShopES> shops = elasticsearchSearchService.searchNearbyShops(latitude, longitude, distance);
        return Result.success(shops);
    }
    
    /**
     * 初始化Elasticsearch数据
     * 将数据库中的所有店铺数据同步到Elasticsearch
     * 
     * @return 操作结果
     */
    @PostMapping("/init-es-data")
    public Result<Boolean> initElasticsearchData() {
        log.info("初始化Elasticsearch数据");
        elasticsearchSyncService.initElasticsearchData();
        return Result.success(true);
    }
    
    /**
     * 创建新店铺
     * 
     * @param shop 店铺信息
     * @return 创建结果
     */
    @PostMapping
    public Result<Shop> createShop(@RequestBody Shop shop) {
        boolean saved = shopService.save(shop);
        if (!saved) {
            log.info("Create shop failed, shop={}", shop);
            return Result.error("CREATE_SHOP_FAILED");
        }
        this.bloomFilterService.addShopId(shop.getId());
        return Result.success(shop);
    }
    
    /**
     * 更新店铺信息
     * 
     * @param shop 店铺信息
     * @return 更新结果
     */
    @PutMapping
    public Result<Shop> updateShop(@RequestBody Shop shop) {
        boolean updated = shopService.updateById(shop);
        if (!updated) {
            log.info("Update shop failed, shop={}", shop);
            return Result.error("UPDATE_SHOP_FAILED");
        }
        String key = "shop:" + shop.getId();
        this.localCache.remove(key);
        this.redisTemplate.delete(key);
        this.kafkaTemplate.send("shop.update",key);
        return Result.success(shop);
    }
    
    /**
     * 删除店铺
     * 
     * @param id 店铺ID
     * @return 删除结果
     */
    @DeleteMapping("/{id}")
    public Result<Void> deleteShop(@PathVariable Long id) {
        boolean deleted = shopService.removeById(id);
        if (!deleted) {
            log.info("Delete shop failed, id={}", id);
            return Result.error("DELETE_SHOP_FAILED");
        }
        String key = "shop:" + id;
        this.localCache.remove(key);
        this.redisTemplate.delete(key);
        this.kafkaTemplate.send("shop.delete",key);
        return Result.success();
    }
    
    /**
     * 查询附近的店铺
     * 
     * @param longitude 经度
     * @param latitude 纬度
     * @param radius 半径（千米）
     * @return 附近的店铺列表
     */
    @GetMapping("/nearby")
    public Result<List<Shop>> getNearbyShops(
            @RequestParam Double longitude,
            @RequestParam Double latitude,
            @RequestParam(defaultValue = "5") Double radius) {
        
        if (longitude == null || latitude == null) {
            log.info("Get nearby shops failed, longitude or latitude is null");
            return Result.error("LONGITUDE_OR_LATITUDE_IS_NULL");
        }
        
        if (radius <= 0) {
            log.info("Get nearby shops failed, radius must be greater than 0");
            return Result.error("RADIUS_MUST_BE_GREATER_THAN_0");
        }
        
        List<Shop> shops = shopService.getNearbyShops(longitude, latitude, radius);
        return Result.success(shops);
    }
    
    /**
     * 根据类型查询店铺
     * 
     * @param type 店铺类型
     * @return 该类型的店铺列表
     */
    @GetMapping("/type/{type}")
    public Result<List<Shop>> getShopsByType(@PathVariable String type) {
        if (type == null || type.trim().isEmpty()) {
            log.info("Get shops by type failed, type is null or empty");
            return Result.error("TYPE_IS_NULL_OR_EMPTY");
        }
        
        List<Shop> shops = shopService.getShopsByType(type);
        return Result.success(shops);
    }
    
    /**
     * 查询营业中的店铺
     * 
     * @return 营业中的店铺列表
     */
    @GetMapping("/active")
    public Result<List<Shop>> getActiveShops() {
        List<Shop> shops = shopService.getActiveShops();
        return Result.success(shops);
    }
    
    /**
     * 查询评分最高的店铺
     * 
     * @param limit 查询数量
     * @return 按评分排序的店铺列表
     */
    @GetMapping("/top-rated")
    public Result<List<Shop>> getTopRatedShops(@RequestParam(defaultValue = "10") Integer limit) {
        if (limit <= 0) {
            log.info("Get top rated shops failed, limit must be greater than 0");
            return Result.error("LIMIT_MUST_BE_GREATER_THAN_0");
        }
        
        List<Shop> shops = shopService.getTopRatedShops(limit);
        return Result.success(shops);
    }

    @PutMapping("/{id}")
    public Result<Void> updateShopAvatar(@PathVariable Long id, @RequestParam MultipartFile avatar) throws Exception {
        if (avatar.isEmpty()) {
            log.error("update shop avatar failed, id={}, because avatar is empty",id);
            return Result.error("AVATAR_IS_EMPTY");
        }
        String avatarUrl = UUID.randomUUID().toString();
        this.minioClient.uploadObject(this.applicationConfiguration.getShopAvatarBucket(),avatarUrl,avatar.getInputStream(),avatar.getSize(),avatar.getContentType());
        boolean update = this.shopService.lambdaUpdate().eq(Shop::getId, id).set(Shop::getAvatarUrl, avatarUrl).update();
        if (!update){
            log.error("update shop avatar failed, id={}, avatarUrl={}, because update shop failed",id,avatarUrl);
            return Result.error("UPDATE_SHOP_AVATAR_FAILED");
        }
        return Result.success();
    }

    @PostMapping("/addScore/{id}/{score}")
    public Result<Void> addScore(@PathVariable Long id, @PathVariable long score) {
        boolean update = this.shopService.addScore(id,score);
        if (!update){
            log.error("add score failed, id={}, score={}, because update shop failed",id,score);
            return Result.error("ADD_SCORE_FAILED");
        }
        return Result.success();
    }

    @GetMapping("/getTop5ByScore/{langtitude}/{latitude}/{radius}")
    public Result<List<Shop>> getTop5ByScore(@PathVariable Double langtitude, @PathVariable Double latitude, @PathVariable Double radius) {
        List<Shop> shops = this.shopService.getTop5ByScore(langtitude,latitude,radius);
        return Result.success(shops);
    }
}